package com.zrrd.mcl.list.service.impl;

import com.alibaba.fastjson.JSON;
import com.zrrd.mcl.list.service.GoodsRepository;
import com.zrrd.mcl.list.service.SearchService;
import com.zrrd.mcl.model.list.*;
import com.zrrd.mcl.model.product.*;
import com.zrrd.mcl.product.client.ProductFeiginClient;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private ProductFeiginClient productFeigClient;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private GoodsRepository goodsRepository;
    @Autowired
    private RestHighLevelClient client;

    @Override
    public void upperGoods(Long skuId) {
        //创建一个Goods商品对象（关联ES商品索引库的）
        Goods goods = new Goods();

        //设置Sku基本信息
        SkuInfo skuInfo = productFeigClient.getSkuInfo(skuId);
        if (skuInfo != null) {
            goods.setTitle(skuInfo.getSkuName());
            goods.setId(skuId);
            goods.setDefaultImg(skuInfo.getSkuDefaultImg());
            goods.setPrice(skuInfo.getPrice().doubleValue());
            goods.setCreateTime(new Date());
        }

        //设置Sku分类信息
        BaseCategoryView categoryView = productFeigClient.getCategoryView(skuInfo.
                getCategory3Id());
        if (categoryView != null) {
            goods.setCategory1Id(categoryView.getCategory1Id());
            goods.setCategory2Id(categoryView.getCategory2Id());
            goods.setCategory3Id(categoryView.getCategory3Id());
            goods.setCategory1Name(categoryView.getCategory1Name());
            goods.setCategory2Name(categoryView.getCategory2Name());
            goods.setCategory3Name(categoryView.getCategory3Name());
        }

        //设置Sku品牌信息
        BaseTrademark trademark = productFeigClient.getTrademark(skuInfo.getTmId());
        if (trademark != null) {
            goods.setTmId(trademark.getId());
            goods.setTmName(trademark.getTmName());
            goods.setTmLogoUrl(trademark.getLogoUrl());
        }

        //设置Sku平台属性信息
        List<BaseAttrInfo> baseAttrInfoList = productFeigClient.getBaseAttrInfo(skuId);
        if (baseAttrInfoList != null && baseAttrInfoList.size() > 0) {
            //创建goods需要的属性集合
            List<SearchAttr> searchAttrList = new ArrayList<>();
            for (BaseAttrInfo baseAttrInfo : baseAttrInfoList) {
                //创建对象
                SearchAttr searchAttr = new SearchAttr();
                searchAttr.setAttrId(baseAttrInfo.getId());
                searchAttr.setAttrName(baseAttrInfo.getAttrName());
                //获取当前循环的平台属性对象的属性集合
                List<BaseAttrValue> attrValueList = baseAttrInfo.getAttrValueList();
                searchAttr.setAttrValue(attrValueList.get(0).getValueName());
                //将对象添加进集合
                searchAttrList.add(searchAttr);
            }
            //设置给goods
            goods.setAttrs(searchAttrList);
        }

        //向es添加数据
        goodsRepository.save(goods);

    }

    @Override
    public void lowerGoods(Long skuId) {
        goodsRepository.deleteById(skuId);
    }

    @Override
    public void updateHotScore(Long skuId) {
        //定义缓存的key
        String hotKey = "hotScore";
        //使用redis的递增命令保存数据
        Double score = redisTemplate.opsForZSet().incrementScore(hotKey, "skuId:" + skuId, 1);
        //只有热度值是10的倍数才更新es
        if (score % 10 == 0) {
            //根据skuId查询es中数据
            Optional<Goods> optional = goodsRepository.findById(skuId);
            Goods goods = optional.get();
            //重新设置热度值
            goods.setHotScore(Math.round(score));
            //重新执行保存
            goodsRepository.save(goods);
        }
    }

    //搜索
    @Override
    public SearchResponseVo search(SearchParam param) throws IOException {
        System.out.println("当前搜索条件====>" + param);
        //1.设置参数
        SearchRequest request = this.buildQueryDsl(param);
        //2.发起请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        System.out.println("查询结果====>" + response);
        //3.处理结果
        SearchResponseVo responseVo = this.parseSearchResult(response);
        //4.设置分页数据
        responseVo.setPageNo(param.getPageNo());
        responseVo.setPageSize(param.getPageSize());
        long totalPages = (responseVo.getTotal() + param.getPageSize() -1) /param.getPageSize();
        responseVo.setTotalPages(totalPages);
        //5.返回结果
        return responseVo;
    }

    //根据用户的搜索条件生成DSL语句
    private SearchRequest buildQueryDsl(SearchParam param) {
        //1.创建搜索请求对象
        SearchRequest request = new SearchRequest("goods");
        //2.创建搜索的查询器对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //3.创建布尔的查询器对象
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //4.根据分类ID过滤
        if (param.getCategory1Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id", param.getCategory1Id()));
        }
        if (param.getCategory2Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id", param.getCategory2Id()));
        }
        if (param.getCategory3Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id", param.getCategory3Id()));
        }
        //5.根据关键字查询
        if (param.getKeyword() != null && !"".equals(param.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", param.getKeyword()));
        }
        //6.根据品牌过滤   2:华为
        String trademark = param.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            String[] split = trademark.split(":");
            if (split != null && split.length == 2) {
                boolQueryBuilder.filter(QueryBuilders.termQuery("tmId", split[0]));
            }
        }
        //7.根据平台属性过滤  23:4G:运行内存  属性id:属性值名称:属性名称
        String[] props = param.getProps();
        if (props != null && props.length > 0) {
            for (String prop : props) {
                String[] split = prop.split(":");
                if (split != null && split.length == 3) {
                    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                    BoolQueryBuilder subBoolQuery = QueryBuilders.boolQuery();
                    subBoolQuery.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                    subBoolQuery.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));
                    boolQuery.must(QueryBuilders.nestedQuery("attrs", subBoolQuery, ScoreMode.None));
                    boolQueryBuilder.filter(boolQuery);
                }
            }
        }
        //8.将bool查询器设置给搜索查询器
        searchSourceBuilder.query(boolQueryBuilder);

        //9.处理分页
        Integer pageNo = param.getPageNo();
        Integer pageSize = param.getPageSize();
        int from = (pageNo - 1) * pageSize;
        searchSourceBuilder.from(from);
        searchSourceBuilder.size(pageSize);

        //10.排序处理  1：热度排序  2：价格排序  ：后面的部分代表排序规则  1：asc   2:desc
        String order = param.getOrder();
        if (!StringUtils.isEmpty(order)) {
            String field = "";
            String[] split = order.split(":");
            if (split != null && split.length == 2) {
                switch (split[0]) {
                    case "1":
                        field = "hotScore";
                        break;
                    case "2":
                        field = "price";
                        break;
                }
                searchSourceBuilder.sort(field, split[1].equals("asc") ? SortOrder.ASC : SortOrder.DESC);
            }
        } else {
            searchSourceBuilder.sort("hotScore", SortOrder.DESC);
        }
        //11.处理高亮
        HighlightBuilder builder = new HighlightBuilder();
        builder.field("title");
        builder.preTags("<span style=color:red>");
        builder.postTags("</span>");
        searchSourceBuilder.highlighter(builder);

        //12.品牌聚合
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("tmIdAgg").field("tmId")
                .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName"))
                .subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl"));
        searchSourceBuilder.aggregation(termsAggregationBuilder);

        //13.平台属性聚合
        NestedAggregationBuilder aggregationBuilder = AggregationBuilders.nested("attrAgg", "attrs")
                .subAggregation(AggregationBuilders.terms("attrIdAgg").field("attrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue")
                        ));
        searchSourceBuilder.aggregation(aggregationBuilder);
        //设置返回指定的字段
        //searchSourceBuilder.fetchSource(new String[]{"id","title","defaultImg","price"},null);

        //给请求对象设置搜索查询器
        request.source(searchSourceBuilder);
        //打印dsl语句
        System.out.println("DSL=====>" + searchSourceBuilder.toString());
        //返回处理好的请求对象
        return request;
    }

    //处理搜索的返回结果
    private SearchResponseVo parseSearchResult(SearchResponse response) {
        //1.创建最终返回的结果对象
        SearchResponseVo vo = new SearchResponseVo();
        //2.获取结果中的商品数据
        SearchHits hits = response.getHits();
        SearchHit[] searchHits = hits.getHits();
        List<Goods> goodsList = new ArrayList<>();
        if (searchHits != null && searchHits.length > 0) {
            for (SearchHit searchHit : searchHits) {
                String source = searchHit.getSourceAsString();
                Goods goods = JSON.parseObject(source, Goods.class);
                //判断是否有高亮数据
                HighlightField field = searchHit.getHighlightFields().get("title");
                if (field != null) {
                    Text text = field.getFragments()[0];
                    goods.setTitle(text.toString());
                }
                goodsList.add(goods);
            }
        }
        //获取聚合结果的整体对象
        Map<String, Aggregation> aggregationMap = response.getAggregations().asMap();
        //3.获取品牌聚合结果
        ParsedLongTerms tmIdAgg = (ParsedLongTerms) aggregationMap.get("tmIdAgg");
        List<SearchResponseTmVo> tmVoList = tmIdAgg.getBuckets().stream().map(bucket -> {
            //创建品牌聚合返回的对象
            SearchResponseTmVo tmVo = new SearchResponseTmVo();
            //设置品牌ID
            String tmId = ((Terms.Bucket) bucket).getKeyAsString();
            tmVo.setTmId(Long.parseLong(tmId));
            //设置品牌名称
            ParsedStringTerms tmNameAgg = ((Terms.Bucket) bucket).getAggregations().get("tmNameAgg");
            String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();
            tmVo.setTmName(tmName);
            //设置品牌Url
            ParsedStringTerms tmLogoUrlAgg = ((Terms.Bucket) bucket).getAggregations().get("tmLogoUrlAgg");
            System.out.println("品牌loho聚合对象====>" + tmLogoUrlAgg);
            String tmLogUrl = tmLogoUrlAgg.getBuckets().get(0).getKeyAsString();
            tmVo.setTmLogoUrl(tmLogUrl);
            //返回当前对象
            return tmVo;
        }).collect(Collectors.toList());
        //4.获取平台属性聚合结果
        ParsedNested attrAgg = (ParsedNested) aggregationMap.get("attrAgg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        List<SearchResponseAttrVo> attrVoList = attrIdAgg.getBuckets().stream().map(bucket -> {
            SearchResponseAttrVo attrVo = new SearchResponseAttrVo();
            //设置属性id
            Number number = ((Terms.Bucket) bucket).getKeyAsNumber();
            attrVo.setAttrId(number.longValue());
            //设置属性名称
            ParsedStringTerms attrNameAgg = ((Terms.Bucket) bucket).getAggregations().get("attrNameAgg");
            attrVo.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());
            //设置属性值
            ParsedStringTerms attrValueAgg = ((Terms.Bucket) bucket).getAggregations().get("attrValueAgg");
            List<String> valueList = attrValueAgg.getBuckets().stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
            attrVo.setAttrValueList(valueList);
            //返回对象
            return attrVo;
        }).collect(Collectors.toList());
        //设置平台属性聚合列表
        vo.setAttrsList(attrVoList);
        //设置品牌聚合列表
        vo.setTrademarkList(tmVoList);
        //设置商品列表数据
        vo.setGoodsList(goodsList);
        //设置总条数
        vo.setTotal(hits.totalHits);
        return vo;
    }
}
